package gnnt.MEBS.mjvm.beans;

import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanServer;
import javax.management.MBeanServerNotification;
import javax.management.Notification;
import javax.management.NotificationListener;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import net.sf.javainetlocator.InetAddressLocator;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.modeler.Registry;
import gnnt.MEBS.mjvm.model.RequestProcessor;
import gnnt.MEBS.mjvm.model.ThreadPool;
import gnnt.MEBS.mjvm.model.jmx.ThreadPoolObjectName;
import gnnt.MEBS.mjvm.tools.JmxTools;

public class ContainerListenerBean
  implements NotificationListener
{
  private Log logger;
  private List poolNames;

  public ContainerListenerBean()
  {
    this.logger = LogFactory.getLog(super.getClass());
    this.poolNames = null; }

  private boolean isInitialized() {
    return (this.poolNames != null);
  }

  private ThreadPoolObjectName findPool(String name)
  {
    Iterator it;
    if ((name != null) && (isInitialized())) {
      for (it = this.poolNames.iterator(); it.hasNext(); ) {
        ThreadPoolObjectName threadPoolObjectName = (ThreadPoolObjectName)it.next();
        if (name.equals(threadPoolObjectName.getThreadPoolName().getKeyProperty("name"))) {
          return threadPoolObjectName;
        }
      }
    }
    return null;
  }

  public synchronized void handleNotification(Notification notification, Object object)
  {
    if (notification instanceof MBeanServerNotification) {
      ThreadPoolObjectName threadPoolObjectName;
      ObjectName objectName = ((MBeanServerNotification)notification).getMBeanName();

      if (notification.getType().equals("JMX.mbean.registered"))
      {
        if ("RequestProcessor".equals(objectName.getKeyProperty("type"))) {
          threadPoolObjectName = findPool(objectName.getKeyProperty("worker"));
          if (threadPoolObjectName != null) threadPoolObjectName.getRequestProcessorNames().add(objectName);
        }
      } else {
        if ((!(notification.getType().equals("JMX.mbean.unregistered"))) || 
          (!("RequestProcessor".equals(objectName.getKeyProperty("type"))))) return;
        threadPoolObjectName = findPool(objectName.getKeyProperty("worker"));
        if (threadPoolObjectName != null)
          threadPoolObjectName.getRequestProcessorNames().remove(objectName);
      }
    }
  }

  private synchronized void initialize()
    throws Exception
  {
    MBeanServer server = new Registry().getMBeanServer();
    Set set = server.queryMBeans(new ObjectName("*:type=ThreadPool,*"), null);
    this.poolNames = new ArrayList(set.size());
    for (Iterator it = set.iterator(); it.hasNext(); )
    {
      ThreadPoolObjectName threadPoolObjectName = new ThreadPoolObjectName();
      ObjectName threadPoolName = ((ObjectInstance)it.next()).getObjectName();

      String name = threadPoolName.getKeyProperty("name");

      threadPoolObjectName.setThreadPoolName(threadPoolName);
      ObjectName grpName = server.getObjectInstance(new ObjectName(threadPoolName.getDomain() + ":type=GlobalRequestProcessor,name=" + name)).getObjectName();

      threadPoolObjectName.setGlobalRequestProcessorName(grpName);

      Set workers = server.queryMBeans(new ObjectName(threadPoolName.getDomain() + ":type=RequestProcessor,*"), null);

      for (Iterator wrkIt = workers.iterator(); wrkIt.hasNext(); ) {
        ObjectName wrkName = ((ObjectInstance)wrkIt.next()).getObjectName();
        if (name.equals(wrkName.getKeyProperty("worker"))) {
          threadPoolObjectName.getRequestProcessorNames().add(wrkName);
        }
      }

      this.poolNames.add(threadPoolObjectName);
    }

    server.addNotificationListener(new ObjectName("JMImplementation:type=MBeanServerDelegate"), this, null, null);
  }

  public synchronized List getThreadPools(boolean includeRequestProcessors)
    throws Exception
  {
    if (!(isInitialized())) initialize();

    List threadPools = new ArrayList(this.poolNames.size());

    MBeanServer server = new Registry().getMBeanServer();

    for (Iterator it = this.poolNames.iterator(); it.hasNext(); )
    {
      ThreadPoolObjectName threadPoolObjectName = (ThreadPoolObjectName)it.next();
      try {
        Iterator wrkIt;
        ObjectName poolName = threadPoolObjectName.getThreadPoolName();

        ThreadPool threadPool = new ThreadPool();
        threadPool.setName(poolName.getKeyProperty("name"));
        threadPool.setMaxThreads(JmxTools.getIntAttr(server, poolName, "maxThreads"));

        if (JmxTools.hasAttribute(server, poolName, "maxSpareThreads")) {
          threadPool.setMaxSpareThreads(JmxTools.getIntAttr(server, poolName, "maxSpareThreads"));
          threadPool.setMinSpareThreads(JmxTools.getIntAttr(server, poolName, "minSpareThreads"));
        }

        threadPool.setCurrentThreadsBusy(JmxTools.getIntAttr(server, poolName, "currentThreadsBusy"));
        threadPool.setCurrentThreadCount(JmxTools.getIntAttr(server, poolName, "currentThreadCount"));

        ObjectName grpName = threadPoolObjectName.getGlobalRequestProcessorName();

        threadPool.setMaxTime(JmxTools.getLongAttr(server, grpName, "maxTime"));
        threadPool.setProcessingTime(JmxTools.getLongAttr(server, grpName, "processingTime"));
        threadPool.setBytesReceived(JmxTools.getLongAttr(server, grpName, "bytesReceived"));
        threadPool.setBytesSent(JmxTools.getLongAttr(server, grpName, "bytesSent"));
        threadPool.setRequestCount(JmxTools.getIntAttr(server, grpName, "requestCount"));
        threadPool.setErrorCount(JmxTools.getIntAttr(server, grpName, "errorCount"));

        if (includeRequestProcessors) {
          for (wrkIt = threadPoolObjectName.getRequestProcessorNames().iterator(); wrkIt.hasNext(); ) {
            ObjectName wrkName = (ObjectName)wrkIt.next();
            try
            {
              RequestProcessor rp = new RequestProcessor();
              rp.setName(wrkName.getKeyProperty("name"));
              rp.setStage(JmxTools.getIntAttr(server, wrkName, "stage"));
              rp.setProcessingTime(JmxTools.getLongAttr(server, wrkName, "requestProcessingTime"));
              rp.setBytesSent(JmxTools.getLongAttr(server, wrkName, "requestBytesSent"));
              rp.setBytesReceived(JmxTools.getLongAttr(server, wrkName, "requestBytesReceived"));
              rp.setRemoteAddr(JmxTools.getStringAttr(server, wrkName, "remoteAddr"));
              rp.setRemoteAddrLocale(InetAddressLocator.getLocale(InetAddress.getByName(rp.getRemoteAddr()).getAddress()));
              rp.setVirtualHost(JmxTools.getStringAttr(server, wrkName, "virtualHost"));
              rp.setMethod(JmxTools.getStringAttr(server, wrkName, "method"));
              rp.setCurrentUri(JmxTools.getStringAttr(server, wrkName, "currentUri"));
              rp.setCurrentQueryString(JmxTools.getStringAttr(server, wrkName, "currentQueryString"));
              rp.setProtocol(JmxTools.getStringAttr(server, wrkName, "protocol"));
              threadPool.addRequestProcessor(rp);
            } catch (InstanceNotFoundException e) {
              this.logger.info("Failed to query RequestProcessor " + wrkName);
              this.logger.debug(e);
            }
          }
        }

        threadPools.add(threadPool);
      } catch (InstanceNotFoundException e) {
        this.logger.error("Failed to query entire thread pool " + threadPoolObjectName);
        this.logger.debug(e);
      }
    }
    return threadPools;
  }

  public synchronized List getThreadPoolNames() throws Exception
  {
    if (!(isInitialized())) initialize();

    List threadPoolNames = new ArrayList(this.poolNames.size());

    for (Iterator it = this.poolNames.iterator(); it.hasNext(); )
    {
      ThreadPoolObjectName threadPoolObjectName = (ThreadPoolObjectName)it.next();
      ObjectName poolName = threadPoolObjectName.getThreadPoolName();
      threadPoolNames.add(poolName.getKeyProperty("name"));
    }
    return threadPoolNames;
  }
}
